
package andre.grids.util;

import java.util.*;
import java.util.ArrayList;
import java.util.Map.Entry;

/**
 *
 * @author andre
 */
public class QueueHashTable<K,V>
        extends Hashtable<K,V> {
    private List<CustomEntry<K,V>> innerList;
    
    public QueueHashTable(int capacity) {
        super(capacity);
        innerList = new ArrayList<CustomEntry<K,V>>(capacity);
    }
    
    public QueueHashTable() {
        this(20);
    }
    
    /*
     * Retira um número de elementos da fila, monta o vetor resultante e depois adiciona os elementos ao final da fila
     * assim o usuário não precisa utilizar outra chamada para enfileirar novamente os elementos
     * @return 
     * */
    public List<V> get(int elements) {
        if (elements <1)
            return null;
        int arrSize = (elements <= innerList.size() ? elements : innerList.size());
        List<V> result = new ArrayList<V>(arrSize);
        for(int x = 0; x < arrSize; x++) {
            CustomEntry<K,V> entry = innerList.remove(0); // retira da primeira posição
            result.add(entry.getValue());
            innerList.add(entry); // acrescenta ao final da lista
        }
        return result;
    }

    @Override
    public void clear() {
        super.clear();
        this.innerList.clear();
    }

    @Override
    public V put(K key, V value) {
        CustomEntry<K,V> entry = new QueueHashTable.CustomEntry(key, value);
        if (super.containsKey(entry)) {
            int idx = innerList.indexOf(entry);
            innerList.add(entry);
        }
        else {
            innerList.add(entry);
        }
        super.put(key, value);
        return value;
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        super.putAll(m);
        Collection<? extends V> elements = m.values();
        Collection<? extends K> keys = m.keySet();
        Iterator it = elements.iterator();
        Iterator itKeys = keys.iterator();
        for(;it.hasNext();) {
            V obj = (V)it.next();
            K key = (K)itKeys.next();
            CustomEntry<K,V> entry = new QueueHashTable.CustomEntry(key, obj);
            int idxOf;
            idxOf = innerList.indexOf(obj);
            if (idxOf < 0) // não foi adicionado à lista
                innerList.add(entry);
            else // já adicionado, deve ser atualizado então
                innerList.set(idxOf, entry);
        }
    }

    @Override
    public V remove(Object key) {
        V obj = super.remove(key);
        if (obj == null)
            return null;
        innerList.remove(new CustomEntry(key, obj));
        return obj;
    }

    @Override
    public synchronized int size() {
        return innerList.size();
    }

    @Override
    public synchronized boolean containsKey(Object key) {
        return super.containsKey(key);
    }
    
    private class CustomEntry<K,V> implements Map.Entry<K, V> {

        private K key;
        private V value;
        
        public CustomEntry(K key, V value ) {
            this.key = key;
            this.value = value;
        }
        
        public final K getKey() {
            return this.key;
        }

        public final V getValue() {
            return this.value;
        }

        public final V setValue(V value) {
            return this.value = value;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj.getClass().equals(this.getClass())) {
                return ((CustomEntry)obj).getKey().equals(this.getKey());
            } else
                return false;
        }

        @Override
        public int hashCode() {
            return getKey().hashCode();
        }
    }
}
